/**
 * 
 */
package de.uni_postdam.ling.tcl.smc_lda;

import it.unimi.dsi.fastutil.longs.LongArrayList;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import de.uni_postdam.ling.tcl.util.SynchronizedStringInterner;

/**
 * @author Christoph Teichmann
 *
 */
public class WordLineDocumentSource implements DocumentSource
{
	/**
	 * 
	 */
	private final Read r;
	
	/**
	 * 
	 */
	private final SynchronizedStringInterner ssi = new SynchronizedStringInterner();
	
	/**
	 * 
	 */
	private final LinkedList<long[]> docs = new LinkedList<long[]>();
	
	/**
	 * 
	 */
	private int size = 5;
	
	/**
	 * @throws IOException 
	 * 
	 */
	public WordLineDocumentSource(Reader documents, int previewSize) throws IOException
	{
		Queue<String> fileNames = new LinkedList<String>();
		
		try(BufferedReader br = new BufferedReader(documents))
		{
			String line;
			while((line = br.readLine()) != null)
			{
				fileNames.add(line.trim());
			}
		}
		
		this.r = new Read(previewSize, fileNames);
		this.r.start();
		
		ensureSize();
	}
	
	/**
	 * 
	 */
	private void ensureSize()
	{
		while(this.docs.size() < this.size)
		{
			long[] doc;
			try
			{
				doc = this.r.source.take();
			}
			catch (InterruptedException e)
			{
				break;
			}
			
			if(doc == null)
			{
				break;
			}
			else
			{
				this.docs.add(doc);
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see de.uni_postdam.ling.tcl.smc_lda.DocumentSource#getDocument(int)
	 */
	@Override
	public long[] getDocument(int offSet)
	{
		return offSet < this.docs.size() ? this.docs.get(offSet) : null;
	}
	/* (non-Javadoc)
	 * @see de.uni_postdam.ling.tcl.smc_lda.DocumentSource#removeBlock(int)
	 */
	@Override
	public void removeBlock(int numberOfEntries)
	{
		for(int i=0;i<numberOfEntries;++i)
		{
			this.docs.poll();
		}
		this.ensureSize();
	}
	
	/* (non-Javadoc)
	 * @see de.uni_postdam.ling.tcl.smc_lda.DocumentSource#hasMoreData()
	 */
	@Override
	public boolean hasMoreData()
	{
		return !this.docs.isEmpty();
	}
	
	/* (non-Javadoc)
	 * @see de.uni_postdam.ling.tcl.smc_lda.DocumentSource#ensureLookAheadSize(int[])
	 */
	@Override
	public void ensureLookAheadSize(int... numbers)
	{
		for(int i : numbers)
		{
			this.size = Math.max(size, i);
		}
	}
	/* (non-Javadoc)
	 * @see de.uni_postdam.ling.tcl.smc_lda.DocumentSource#getInterner()
	 */
	@Override
	public SynchronizedStringInterner getInterner()
	{
		return this.ssi;
	}
	
	
	/* (non-Javadoc)
	 * @see de.uni_postdam.ling.tcl.smc_lda.DocumentSource#shutDown()
	 */
	@Override
	public void shutDown()
	{
		this.r.interrupt();
	}

	/**
	 * 
	 * @author Christoph Teichmann
	 *
	 */
	private class Read extends Thread
	{
		
		/**
		 * 
		 */
		private final BlockingQueue<long[]> source;
		
		/**
		 * 
		 */
		private final Queue<String> toDo;
		
		/**
		 * @param die
		 * @param toDo
		 */
		public Read(int previewSize, Queue<String> toDo)
		{
			super();
			this.toDo = toDo;
			this.source = new ArrayBlockingQueue<long[]>(previewSize);
		}

		/*
		 * (non-Javadoc)
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run()
		{
			LongArrayList lal = new LongArrayList();
			
			while(true)
			{	
				String fileName = this.toDo.poll();
				if(fileName == null)
				{
					try
					{
						this.source.put(null);
					}
					catch (InterruptedException e)
					{
						break;
					}
				}
				
				BufferedReader br;
				
				try
				{
					br = new BufferedReader(new FileReader(fileName));
				}
				catch(FileNotFoundException fnfe)
				{
					br = new BufferedReader(
							new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName)));
				}
				
				try
				{
					String line;
					lal.clear();
					while((line = br.readLine()) != null)
					{
						lal.add(ssi.get(line));
					}
				}
				catch (IOException e)
				{
					e.printStackTrace();
					continue;
				}
				
				try
				{
					this.source.put(lal.toLongArray());
				}
				catch (InterruptedException e)
				{
					break;
				}
			}
		}
	}
}
